Welcome to the developer documentation for the WebUI API (compatible version). WebUI allows GTA V mod developers to create immersive, web-based user interfaces (HTML/CSS/JS) directly within the game using CEF (Chromium Embedded Framework).
This documentation covers both the C# (ScriptHookVDotNet) backend API and the JavaScript frontend SDK.
WebUI operates on a multi-layer architecture:
id.console.log interception, parsed natively, and routed to C# events.Namespace: WebUI
Class: API (Inherits from Script)
The C# API provides a static interface to create, manage, and communicate with web layers.
bool CreateLayer(int id, string url, bool visible = false)Creates a new UI layer.
id: Unique integer identifier for the layer.url: The URL to load (e.g., file:///C:/my_mod/ui/index.html or http://localhost:3000).visible: Initial visibility state.true if successfully created.void Reload(int id)Reloads the current URL for the specified layer.
void Close(int id)Closes and destroys the specified layer.
void CloseAll()Closes and destroys all active layers.
void SetVisible(int id, bool state)Shows or hides the specified layer.
void SetFocus(int id, bool captureKeyboard, bool captureMouse)Sets input focus to a specific layer. This also activates global input traps so the game doesn't process the inputs while the UI is focused.
id: The target layer.captureKeyboard: Route keyboard strokes to the UI.captureMouse: Route mouse movements/clicks to the UI.void SetClickThrough(int id, bool passInputsToGame)Allows mouse clicks to pass through the UI directly into the game world (useful for HUDs).
void CaptureMouseInput(bool toggle)Globally forces mouse input capture for WebUI.
void CaptureKeyboardInput(bool toggle)Globally forces keyboard input capture for WebUI.
void SetZOrder(int id, int order)Explicitly sets the Z-Order (depth/stacking order) of a layer.
void SendToFront(int id)Brings the specified layer to the very front of all UI layers.
void SendToBack(int id)Sends the specified layer to the very back of all UI layers.
void SendMessage(int id, string eventName, object payloadData)Sends a JSON-serializable payload to the specified layer. The JavaScript side can listen for this using WebUI.listen().
id: Target layer.eventName: The event name string to emit.payloadData: Any C# object (will be serialized to JSON dynamically).void ExecuteJS(int id, string javascriptPayload)Executes raw JavaScript code directly in the context of the layer's webpage.
event WebUIEventHandler OnMessageReceivedA static C# event that triggers when the JavaScript side emits data back to C#.
Delegate Signature:
public delegate void WebUIEventHandler(int layerId, string eventName, dynamic parsedPayload);
layerId: The ID of the layer that sent the message.eventName: The event name emitted by JS (defaults to event_type or event in the JSON payload).parsedPayload: The dynamic JSON payload sent from JavaScript.To communicate with the C# backend, include the WebUI_SDK.js in your HTML file. It exposes a global window.WebUI object.
WebUI.listen(eventName, callback)Listens for messages sent from the C# backend via API.SendMessage().
eventName (string): The name of the event to listen for.callback (function): Executed when the event is received. Passes the payload as the first argument.WebUI.listen("UPDATE_SCORE", function(payload) {
console.log("New score: " + payload.score);
});
WebUI.emit(eventName, data = {})Sends data from the webpage to the C# backend. Triggers the OnMessageReceived event in C#.
eventName (string): The name of the event.data (object): The data payload to send.WebUI.emit("PLAYER_READY", { username: "JohnDoe" });
WebUI.ready()Highly Recommended. Emits a built-in system event (SYSTEM_LAYER_READY) telling C# that the HTML/DOM framework (Vue, React, Svelte, etc.) is fully loaded and ready to receive initialization data.
window.addEventListener('DOMContentLoaded', (event) => {
WebUI.ready();
});
WebUI.dropFocus()Emits a built-in system event (SYSTEM_DROP_FOCUS) that forces the C# engine to release keyboard/mouse input capture. This allows the player to move the GTA V camera again without closing the UI.
document.getElementById('backBtn').addEventListener('click', () => {
WebUI.dropFocus();
});
WebUI.close()Emits a built-in system event (SYSTEM_CLOSE_LAYER) instructing C# to destroy/hide the current layer.
document.getElementById('exitBtn').addEventListener('click', () => {
WebUI.close();
});
The WebUI backend natively listens for the following events emitted from JS via WebUI.emit():
| Event Name | Description |
|---|---|
SYSTEM_LAYER_READY |
Notifies C# that the webpage is loaded and ready for data injection. |
SYSTEM_DROP_FOCUS |
Releases hardware input capture (keyboard/mouse) back to GTA V. |
SYSTEM_CLOSE_LAYER |
Triggers API.Close(id) on the C# side for the emitting layer. |
ui.html)<!DOCTYPE html>
<html>
<head>
<title>My WebUI</title>
<script src="WebUI_SDK.js"></script>
</head>
<body>
<h1 id="title">Hello GTA V!</h1>
<button onclick="returnToGame()">Release Focus</button>
<script>
// 1. Tell C# we are ready
WebUI.ready();
// 2. Listen for data from C#
WebUI.listen("SET_TITLE", function(payload) {
document.getElementById('title').innerText = payload.newTitle;
});
// 3. Send data to C#
function returnToGame() {
WebUI.emit("BUTTON_CLICKED", { buttonId: 1 });
WebUI.dropFocus(); // Let the player move the camera again
}
</script>
</body>
</html>
MyMod.cs)using System;
using GTA;
using WebUI;
public class MyWebUIMod : Script
{
private const int UI_ID = 1;
private string url = "file:///C:/MyMod/ui.html";
public MyWebUIMod()
{
// Subscribe to JS events
API.OnMessageReceived += HandleWebUIEvent;
// Create and show the layer
API.CreateLayer(UI_ID, url, true);
// Capture input so the user can interact with the webpage
API.SetFocus(UI_ID, true, true);
}
private void HandleWebUIEvent(int layerId, string eventName, dynamic parsedPayload)
{
if (layerId != UI_ID) return;
if (eventName == "BUTTON_CLICKED")
{
GTA.UI.Notification.Show($"Button {parsedPayload.buttonId} was clicked!");
}
}
private void OnTick(object sender, EventArgs e)
{
// Example: Send data to JS when a specific key is pressed
if (Game.IsKeyPressedJustPressed(Keys.Y))
{
API.SendMessage(UI_ID, "SET_TITLE", new { newTitle = "Key Y was pressed!" });
}
}
}